﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using SimpleRest.WebTest.Entities;
using SimpleRest.WebTest.Handlers;
using SimpleRest.Core.Routing;
using SimpleRest.Core.Routing;
using SimpleRest.Core;
using SimpleRest.Core.Extensions.Documentation;
using SimpleRest.Core.Extensions;
using SimpleRest.Core.Exceptions;
using SimpleRest.Core.Extensions.OAuth;

namespace SimpleRest.WebTest.Services
{
    [GeneralDescription("Provides ability to perform actions against the product model within this domain.")] // Used to create more description for OPTIONS request against this service
    [LinkDescription("http://api.localhost.com", "Api documentation home")] // Used to create more description for OPTIONS request against this service
    [LinkDescription("http://dev.localhost.com", "Developer portal for Api")] // Used to create more description for OPTIONS request against this service
    [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.NotFound, Description = "Product resource not found")] // Used to create more description for OPTIONS request against this service
    [Service("products")] // This attribute indicates to the SimpleRest platform that this class is a service
    public class ProductsService : ServiceBase
    {
        public ProductsService()
        {
            // mapping handler demonstrating all methods with an input parameter named "filter" will have a value set by this mapping handler
            base.AddMapping(new FilterMappingHandler("filter"));
            // mapping handler demonstrating that all methods with with input parameter named 
            //"complexType", dictated within the handler, will have a value set by this mapping handler.
            base.AddMapping(new ComplexTypeTestMappingHandler());
        }

        /// <summary>
        /// Very simply a list of dummy product objects.
        /// </summary>
        private static List<Product> Items = new List<Product>()
        {
            Product.GetMock("0"),
            Product.GetMock("1"),
            Product.GetMock("2"),
            Product.GetMock("3"),
            Product.GetMock("4"),
            Product.GetMock("5"),
            Product.GetMock("6"),
            Product.GetMock("7")
        };

       // [ProtectedResource(ResourceKey = "GetAllProducts")]
        [GeneralDescription("Returns all product resources.")] // Used to create more description for OPTIONS request against this service
        [ParameterDescription("page", typeof(int), true, "Allows consumer to specify the page in which data is returned.  This is zero based.")] // Used to create more description for OPTIONS request against this service
        [ParameterDescription("pageCount", typeof(int), true, "Allows consumer to specify the page count in which data is returned.  This is zero based.")] // Used to create more description for OPTIONS request against this service
        [ParameterDescription("filter", typeof(string), true, "Specify filter criteria for returned product resource.  ex: ?Format={A, B, C}")] // Used to create more description for OPTIONS request against this service
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.OK, Description = "Returned with the request was successful and product resources were found.")]
        [LinkDescription("http://api.localhost.com/Filtering.htm", "Api documentation on filter.  What it does, examples, and how it works.")] // Used to create more description for OPTIONS request against this service
        [WebGet(UriTemplate = "?page={page}&pageCount={pageCount}")] // dictates the route to which this operation is invoked. Query string parameters are always optional.
        public OperationResponse<ProductCollection> GetAll(int? page, int? pageCount, Filter filter/* pumped in by mapping handler */, ComplexTypeObject complexType = null /* pumped in by mapping handler*/)
        {
            if (complexType == null)
                throw new Exception("Complex type was not set to instance of an object.");

            if (page.HasValue && page.Value > -1 && pageCount.HasValue && pageCount.Value > 0)
            {
                List<Product> products = new List<Product>();

                int startAt = page.Value * pageCount.Value;
                for (int i = startAt; i < startAt + pageCount.Value; i++)
                {
                    if (Items.Count > i)
                        products.Add(Items[i]);
                    else
                        break;
                }
                return new OperationResponse<ProductCollection>(new ProductCollection(products));
            }
            return new OperationResponse<ProductCollection>(new ProductCollection(Items));
        }


        [GeneralDescription("Returns a single product by specifying the id in the URI")]
        [ParameterDescription("id", typeof(string), false, "The id associated to the product resource in which you wish to return.")]
        [StatusCodeDescription(ErrorCode = "100.121", StatusCode = System.Net.HttpStatusCode.NotFound)]// Used to create more description for OPTIONS request against this service
        [WebGet(UriTemplate = "{id}")] // dictates the route to which this operation is invoked.
        public Product Get(string id)
        {
            var item = Items.FirstOrDefault(d => d.Id == id);
            if (item == null)
                throw new WebException(System.Net.HttpStatusCode.NotFound, "100.121");
                //return new OperationResponse<Product>(System.Net.HttpStatusCode.NotFound);
            //return new OperationResponse<Product>(item);
            return item;
        }

        [GeneralDescription("Returns only the price sub-resource for the product resource associated to the id parameter in the URI")]
        [ParameterDescription("id", typeof(string), false, "The id associated to the product resource in which you wish to return.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.OK, Description="Status code returned when the price was found.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.NotFound, Description = "Status code returned when the product resource for the associated URI was not found.")]
        [WebGet(UriTemplate = "{id}/Price")] // dictates the route to which this operation is invoked.
        public OperationResponse<Price> GetPrice(string id)
        {
            var item = Items.FirstOrDefault(d => d.Id == id);
            if (item == null)
                return new OperationResponse<Price>(System.Net.HttpStatusCode.NotFound);
            var price = item.Price;
            if (price == null)
                return new OperationResponse<Price>(System.Net.HttpStatusCode.NotFound);
            return new OperationResponse<Price>(price);
        }

        [GeneralDescription("Returns the content sub-resource for the associated product resource specified by the URI.")]
        [ParameterDescription("id", typeof(string), false, "The id associated to the product resource in which you wish to return.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.OK, Description = "Status code returned when the price was found.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.NotFound, Description = "Status code returned when the product resource for the associated URI was not found.")]
        [WebGet(UriTemplate = "{id}/Content")] // dictates the route to which this operation is invoked.
        public OperationResponse<Content> GetContent(string id)
        {
            var item = Items.FirstOrDefault(d => d.Id == id);
            if (item == null)
                return new OperationResponse<Content>(System.Net.HttpStatusCode.NotFound);
            var content = item.Content;
            if (content == null)
                return new OperationResponse<Content>(System.Net.HttpStatusCode.NotFound);

            return new OperationResponse<Content>(content);
        }

        [GeneralDescription("Deletes the product resource associated to the ID supplied in the URI")]
        [ParameterDescription("id", typeof(string), false, "The id associated to the product resource in which you wish to return.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.OK, Description = "Status code returned when the price was found.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.NotFound, Description = "Status code returned when the product resource for the associated URI was not found.")]
        [WebDelete(UriTemplate = "{id}")] // dictates the route to which this operation is invoked.
        public OperationResponse Delete(string id)
        {
            var item = Items.FirstOrDefault(d => d.Id == id);
            if (item == null)
                return new OperationResponse(System.Net.HttpStatusCode.NotFound);
            Items.Remove(item);
            return new OperationResponse(System.Net.HttpStatusCode.OK);
        }

        [GeneralDescription("Creates a product resource using input parameters set in the http body.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.OK, Description = "Status code returned when the price was found.")]
        [StatusCodeDescription(StatusCode = System.Net.HttpStatusCode.NotFound, Description = "Status code returned when the product resource for the associated URI was not found.")]
        [WebPost(UriTemplate = "")] // dictates the route to which this operation is invoked.
        public OperationResponse<string> Create([HttpBody]Product item)
        {
            if (item == null)
                return new OperationResponse<string>(System.Net.HttpStatusCode.NoContent);

            item.Id = Guid.NewGuid().ToString();
            Items.Add(item);
            return new OperationResponse<string>(item.Id, System.Net.HttpStatusCode.Created);
        }

        [WebPost(UriTemplate = "{something}")]
        public OperationResponse Create2(string something)
        {
            return new OperationResponse(System.Net.HttpStatusCode.NoContent);
        }

        /// <summary>
        /// When you inherit ServiceBase you gain visibility at this level into which operation threw an exception.  This
        /// method will fire for all methods in the event the execution threw an exception.  The error handler set by the Configuration
        /// object when this service was registered will still fire as well.
        /// </summary>
        /// <param name="targetOperation"></param>
        /// <param name="ex"></param>
        public override void OperationExecutionException(Operation targetOperation, Exception ex)
        {
            // put a break point here!
            base.OperationExecutionException(targetOperation, ex);
        }

        /// <summary>
        /// When you inherit ServiceBase you gain visibility at this level into which operation was just executed.  This
        /// method will fire for all methods after the method is executed.
        /// </summary>
        /// <param name="targetOperation"></param>
        /// <param name="value"></param>
        public override void OperationPostExecution(Operation targetOperation, object value)
        {
            // put a break point here!
            base.OperationPostExecution(targetOperation, value);
        }

        /// <summary>
        /// When you inherit ServiceBase you gain visibility at this level into which operation is about to be executed.  This
        /// method will fire for all methods before the method is executed.
        /// </summary>
        /// <param name="targetOperation"></param>
        public override void OperationPreExecution(Operation targetOperation)
        {
            // put a break point here!
            base.OperationPreExecution(targetOperation);
        }
    }
}